<template>
  <div
    ref="containerRef"
    class="_battle-scroll-wrapper"
    :class="{ _mobile: props.isMobile, [`_user-${props.userNum}`]: true }"
  >
    <div class="_select-bg">
      <img
        v-if="!baseAnimationInfo.isAnimation"
        class="_select-bg-img"
        :src="BoxBg"
      />
    </div>

    <ul
      class="_animation-list"
      :style="{ transform: `translateY(${baseAnimationInfo.currentY}px)` }"
    >
      <li
        class="_animation-item"
        :class="{ _active: baseAnimationInfo.tickIndex === idx }"
        v-for="(item, idx) in productList"
        :key="`${item.product?.id}-${idx}`"
      >
        <img :src="item?.product?.image" />
        <div
          class="_text-wraper"
          :class="{
            _active:
              !baseAnimationInfo.isAnimation &&
              baseAnimationInfo.tickIndex === idx,
          }"
        >
          <div class="_text-ellipsis _text-small">
            {{ item?.product?.name }}
          </div>
          <div
            class="_text-ellipsis"
            :class="{
              '_text-middle': !props.isMobile,
              '_text-small': props.isMobile,
            }"
          >
            $ {{ item?.product?.amount }}
          </div>
        </div>
      </li>
    </ul>
  </div>
</template>

<script lang="tsx" setup>
import BoxBg from "@/assets/box-bg.png";
import { cubicBezier } from "@/utils/cubicBezier";

import type { BattleScrollItemExpose } from "./battleScrollItem";
import {
  battleScrollItemEmits,
  battleScrollItemProps,
} from "./battleScrollItem";

const props = defineProps(battleScrollItemProps);
const emits = defineEmits(battleScrollItemEmits);

const containerRef = ref<HTMLDivElement>();
const baseAnimationInfo = reactive({
  containerHeight: 0,
  productHeight: 0,
  currentY: 0,
  targetY: 0,
  selectIndex: 0,
  time: 5,
  backTime: 0.3,
  excessiveTime: 0.5,
  excessiveDistance: 20,
  startTime: new Date().getTime(),
  isAnimation: false,
  animationId: 0,
  diff: 0,
  cubicBezier: cubicBezier(0.24, 0.89, 0.0, 1),
  endCallback: null,
  tickIndex: -1,
  isInit: true,
});

const showProducts = ref<API.Battles.BattleProductItem[]>([]);
const hitProducts = ref<API.Battles.BattleProductItem[]>([]);

const productList = computed(() => {
  const products = [...hitProducts.value, ...showProducts.value];
  const result = products.map((item, idx: number) => {
    const middle = 0.5 * baseAnimationInfo.productHeight;
    const middleTop = idx * baseAnimationInfo.productHeight + middle;
    const middleBottom =
      (products.length - 1 - idx) * baseAnimationInfo.productHeight + middle;
    return {
      product: item,
      middleTop: middleTop,
      middleBottom: middleBottom,
      section: [middleTop - middle, middleTop + middle],
    };
  });
  return result;
});

// 获取初始化信息
async function getInitInfo() {
  if (baseAnimationInfo.isInit === false) {
    return;
  }
  await nextTick();
  const list = containerRef.value.querySelector("._animation-list");
  const listItem = list.children?.[0] as HTMLLIElement;
  const containerHeight = containerRef.value.getBoundingClientRect().height;
  const halfContainerHeight = containerHeight / 2;

  baseAnimationInfo.containerHeight = containerHeight;
  baseAnimationInfo.productHeight = listItem.getBoundingClientRect().height;
  baseAnimationInfo.isInit = false;
  // 寻找当前选中的
  for (
    let selectIdx = productList.value.length - 1;
    selectIdx >= 0;
    selectIdx--
  ) {
    if (productList.value[selectIdx].middleBottom > halfContainerHeight) {
      baseAnimationInfo.selectIndex = selectIdx;
      baseAnimationInfo.tickIndex = selectIdx;
      baseAnimationInfo.currentY = -productList.value[selectIdx].middleTop;
      break;
    }
  }
}

function animationStart() {
  baseAnimationInfo.tickIndex = -1;
  emits("start");
  const base = Math.random() > 0.5 ? 1 : -1;
  const diffInstance = Math.max(
    Math.random() * (baseAnimationInfo.productHeight / 2),
    30
  );
  baseAnimationInfo.diff = Math.ceil(diffInstance * base);
  baseAnimationInfo.startTime = performance.now();
  baseAnimationInfo.isAnimation = true;
  baseAnimationInfo.animationId = window.requestAnimationFrame(animationRender);
}

function animationEnd() {
  baseAnimationInfo.isAnimation = false;
  window.cancelAnimationFrame(baseAnimationInfo.animationId);
  emits("end", props.userId);
  baseAnimationInfo.endCallback?.();
}

async function animationRender() {
  if (baseAnimationInfo.isAnimation) {
    const diffTime = performance.now() - baseAnimationInfo.startTime;
    const time = diffTime / baseAnimationInfo.time / 1000;
    const target = baseAnimationInfo.targetY + baseAnimationInfo.diff;
    const scrollLength =
      -(productList.value.length * baseAnimationInfo.productHeight) - target;
    if (time < 1) {
      baseAnimationInfo.currentY =
        (1 - baseAnimationInfo.cubicBezier(time)) * (scrollLength / 2) + target;
      onTick(baseAnimationInfo.currentY);
      window.requestAnimationFrame(animationRender);
    } else {
      baseAnimationInfo.currentY = target;
      baseAnimationInfo.startTime = performance.now();
      animationEndRender();
    }
  }
}

function animationEndRender() {
  const diffTime = performance.now() - baseAnimationInfo.startTime;
  const endTime = diffTime / (baseAnimationInfo.backTime * 1000);
  if (endTime < 1) {
    baseAnimationInfo.currentY =
      baseAnimationInfo.targetY +
      baseAnimationInfo.diff * (1 - endTime < 0 ? 0 : 1 - endTime);
    window.requestAnimationFrame(animationEndRender);
  } else {
    baseAnimationInfo.currentY = baseAnimationInfo.targetY;
    animationEnd();
  }
}

function onTick(current: number) {
  current = Math.abs(current) + baseAnimationInfo.containerHeight / 2;
  for (let idx = 0; idx < productList.value.length; idx++) {
    const section = productList.value[idx].section;
    if (current >= section[0] && current < section[1]) {
      if (baseAnimationInfo.tickIndex !== idx) {
        baseAnimationInfo.tickIndex = idx;
        emits("tick", idx);
      }
      break;
    }
  }
}

defineExpose<BattleScrollItemExpose>({
  start: async (spin) => {
    const { hit_scroll_product_list_index, hit_scroll_product_list } =
      spin?.spin_result || {};
    showProducts.value = (spin.scroll_product_list || []).reverse();
    hitProducts.value = (hit_scroll_product_list || []).reverse();
    await getInitInfo();
    baseAnimationInfo.selectIndex = hit_scroll_product_list_index;
    baseAnimationInfo.targetY =
      baseAnimationInfo.containerHeight * 0.5 -
      productList.value[baseAnimationInfo.selectIndex].middleTop;
    animationStart();
  },
  getUserId: () => props.userId,
});
</script>

<style lang="less" scoped>
._battle-scroll-wrapper {
  @apply relative h-full;
  width: 8.25rem;
  ul {
    @apply flex flex-col m-0 p-0 w-full list-none;
    li {
      @apply relative flex flex-col items-center justify-center w-full;
      padding: 0.94rem 0.94rem 2.4rem 0.94rem;
      width: 100%;
      aspect-ratio: 1/1.2;
      min-height: 0;
      img {
        @apply flex-1 h-0;
        aspect-ratio: 1/1;
        min-height: 0;
        object-fit: contain;
        transform: scale(0.75);
      }

      ._text-wraper {
        @apply absolute flex flex-col w-full;
        left: 0;
        right: 0;
        bottom: -0.3rem;
        gap: 0.31rem;
        opacity: 0;
        div {
          @apply text-center;
        }

        &._active {
          opacity: 1;
        }
      }

      &._active {
        img {
          transform: scale(1);
        }
      }
    }
  }

  ._select-bg {
    @apply absolute flex flex-col items-center justify-between w-full;
    top: 50%;
    left: 0;
    z-index: 0;
    aspect-ratio: 1 / 1;
    min-height: 0;
    transform: translateY(-50%);
    ._select-bg-img {
      @apply absolute top-0 left-0 bottom-0 right-0 w-full h-full;
      object-fit: contain;
      transform: scale(1.6);
    }
  }

  &._mobile {
    width: 100%;
    height: 100%;
    ul {
      width: 100%;
      height: 100%;
      li {
        width: 100%;
        aspect-ratio: unset;
        min-height: auto;
        height: calc(100% / 3);
        img {
          width: 100%;
          transform: scale(0.8);
        }

        ._text-wraper {
          gap: 0.15rem;
          transform: scale(1.1);
        }
      }
    }

    &._user-4 {
      ul {
        li {
          padding: 0.5rem 0.5rem 2.2rem 0.5rem; 
          img {
            transform: scale(1);
          }
          &._active {
            img {
              transform: scale(1.4);
            }
          }
        }
      }
    }


    &._user-3 {
      ul {
        li {
          padding: 0.5rem 0.5rem 1.8rem 0.5rem;
        }
      }
    }

    &._user-2 {
      ul {
        li {
          padding: 0.5rem 0.5rem 1.5rem 0.5rem;
          &._active {
            img {
              transform: scale(1.1);
            }
          }
        }
      }
    }
  }
}
</style>
